package com.winning.sx.microframework.common.util;

import java.util.Date;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

/**
 * JWT工具类
 * Created by Kong on 2019-02-19.
 * JWT由3个部分组成,分别是 头部Header,载荷Payload一般是用户信息和声明,签证Signature一般是密钥和签名
 * 当头部用base64进行编码后一般都会呈现eyJ...形式,而载荷为非强制使用,签证则包含了哈希算法加密后的数据,包括转码后的header,payload和sercetKey
 * payload包含几个部分,issuer签发者,subject面向用户,iat签发时间,exp过期时间,aud接收方
 * subject、issuer也可自定义传入，此处作为默认值处理
 * <p>
 * 如果access_token过期，refresh_token未过期，可以通过refresh_token调用refreshToken接口返回access_token，更新客户端的access_token，以后通过新的access_token访问WebbApi方法
 * 如果access_token过期，refresh_token也过期，所有WebApi方法无法访问。就只能重新uid+密钥调用getAccessToken放回access_token
 */
public class JwtUtil {

	/**
	 * 秘钥，token加密时使用
	 * TODO 各web应用服务端自定义
	 * 一旦得到该密钥也就可以伪造token了
	 */
	private static String key = "WkskpDhSkuBUOP*ITB*123123123";

	/**
	 * access_token的过期时间。一般一天或几个小时更新token，看项目需要
	 * TODO 各业务系统自行设置过期时间，不宜太长
	 */
	private final static long exp = 1000 * 60 * 60;//1小时
	/**
	 * refresh_token的过期时间
	 */
	private final static long exp_ref = 1000 * 60 * 60 * 5;//5小时

	/**
	 * 生成jwt，返回access_token和refresh_token
	 *
	 * @param uid 访问用户id
	 * @return jwt [access_token, refresh_token]
	 */
	public static String[] createJwt(String uid) {
		Long now = System.currentTimeMillis();
		JwtBuilder jwtBuilder = Jwts.builder().setId(uid)
				//使用Hash256算法进行加密。将密钥转码为base64形式,再转为字节码。
				.signWith(SignatureAlgorithm.HS256, new SecretKeySpec(DatatypeConverter.parseBase64Binary(key), SignatureAlgorithm.HS256.getJcaName()))
				.setIssuer("issure")
				.setSubject("subject")
				.setExpiration(new Date(now + exp));
		JwtBuilder jwtBuilder_ref = Jwts.builder().setId(uid)
				.signWith(SignatureAlgorithm.HS256, new SecretKeySpec(DatatypeConverter.parseBase64Binary(key), SignatureAlgorithm.HS256.getJcaName()))
				.setIssuer("issure")
				.setSubject("subject")
				.setExpiration(new Date(now + exp_ref));
		return new String[]{jwtBuilder.compact(), jwtBuilder_ref.compact()};
	}

	public static String[] verifyJwt(String token) {
		Claims claims;
		//将token解密出来,将payload信息包装成Claims类返回
		try {
			claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(key)).parseClaimsJws(token).getBody();
			if (claims.containsKey("exp") && (claims.getExpiration().getTime() < new Date().getTime())) {
				return new String[]{GrantState.TOKEN_EXPIRED.toString(), ""};
			}
		} catch (ExpiredJwtException e) {
			e.printStackTrace();
			return new String[]{GrantState.TOKEN_EXPIRED.toString(), ""};
		} catch (Exception e) {
			e.printStackTrace();
			return new String[]{GrantState.TOKEN_INVALID.toString(), ""};
		}
		return new String[]{GrantState.OK.toString(), claims.getId()};
	}

	/**
	 * 通过refresh_token刷新access
	 *
	 * @param refresh_token refresh_token
	 * @return access_token
	 */
	public static String refreshToken(String refresh_token) {
		//Claims就是包含了Payload的信息类
		Claims claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(key)).parseClaimsJws(refresh_token).getBody();
		if (claims == null)
			return "";
		String subject = claims.getSubject();
		String issuer = claims.getIssuer();

		JwtBuilder jwtBuilder = Jwts.builder().setId(claims.getId())
				//使用Hash256算法进行加密。将密钥转码为base64形式,再转为字节码。
				.signWith(SignatureAlgorithm.HS256, new SecretKeySpec(DatatypeConverter.parseBase64Binary(key), SignatureAlgorithm.HS256.getJcaName()))
				.setIssuer(issuer)
				.setSubject(subject)
				.setExpiration(new Date(System.currentTimeMillis() + exp));
		//生成新的token,根据现在的时间
		return jwtBuilder.compact();
	}

}